package com.usefullc.crawler.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.usefullc.common.util.BeanUtils;
import com.usefullc.common.util.JsonUtil;
import com.usefullc.common.util.http.HttpNewUtils;
import com.usefullc.common.util.http.RequestRes;
import com.usefullc.common.util.http.ResponseRes;
import com.usefullc.crawler.component.DatabaseComponent;
import com.usefullc.crawler.component.ExcelGenerator;
import com.usefullc.crawler.domain.ActiveInstance;
import com.usefullc.crawler.domain.ActiveProcess;
import com.usefullc.crawler.domain.OutputTemplate;
import com.usefullc.crawler.enums.DatabaseTypeEnum;
import com.usefullc.crawler.enums.OutputTemplateTypeEnum;
import com.usefullc.crawler.mapper.OutputTemplateMapper;
import com.usefullc.crawler.query.OutputTemplateQuery;
import com.usefullc.crawler.selenium.CrawlerWebDriver;
import com.usefullc.crawler.selenium.CrawlerWebDriverBuilder3;
import com.usefullc.crawler.vo.OutputContext;
import com.usefullc.crawler.vo.OutputTemplateVo;
import com.usefullc.system.entity.Pager;
import com.usefullc.system.service.AbstractBizService;
import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.WebDriver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 爬虫输出模板服务类
 * @author: 星空
 * @date: 2022-12-15
 */
@Service
public class OutputTemplateService extends AbstractBizService<OutputTemplateMapper, OutputTemplate, OutputTemplateQuery> {
    @Autowired
    private DatabaseComponent databaseComponent;

    @Autowired
    private CrawlerWebDriverBuilder3 crawlerWebDriverBuilder;

    private String baseScript;

    @Autowired
    private ActiveProcessService activeProcessService;

    {
        baseScript = "function calculateTimeAgo(str) {\n" +
                "  const now = new Date();\n" +
                "\n" +
                "  if (str.includes('秒钟')) {\n" +
                "    const seconds = parseInt(str);\n" +
                "    return new Date(now - seconds * 1000);\n" +
                "  } else if (str.includes('分钟')) {\n" +
                "    const minutes = parseInt(str);\n" +
                "    return new Date(now - minutes * 60 * 1000);\n" +
                "  } else if (str.includes('小时')) {\n" +
                "    const hours = parseInt(str);\n" +
                "    return new Date(now - hours * 60 * 60 * 1000);\n" +
                "  } else if (str.includes('天')) {\n" +
                "    const days = parseInt(str);\n" +
                "    return new Date(now - days * 24 * 60 * 60 * 1000);\n" +
                "  } else if (str.includes('周')) {\n" +
                "    const weeks = parseInt(str);\n" +
                "    return new Date(now - weeks * 7 * 24 * 60 * 60 * 1000);\n" +
                "  } else if (str.includes('月')) {\n" +
                "    const months = parseInt(str);\n" +
                "    return new Date(now.getFullYear(), now.getMonth() - months, now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds());\n" +
                "  } else if (str.includes('年')) {\n" +
                "    const years = parseInt(str);\n" +
                "    return new Date(now.getFullYear() - years, now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds());\n" +
                "  }\n" +
                "\n" +
                "  return null; // 无法解析时间\n" +
                "}\n"+

                "function formatDateTime(date, format) {\n" +
                        "  const formatTokens = {\n" +
                        "    'yyyy': date.getFullYear(),\n" +
                        "    'MM': padZero(date.getMonth() + 1),\n" +
                        "    'dd': padZero(date.getDate()),\n" +
                        "    'HH': padZero(date.getHours()),\n" +
                        "    'mm': padZero(date.getMinutes()),\n" +
                        "    'ss': padZero(date.getSeconds())\n" +
                        "  };\n" +
                        "\n" +
                        "  let formattedDateTime = format;\n" +
                        "\n" +
                        "  for (const token in formatTokens) {\n" +
                        "    if (formatTokens.hasOwnProperty(token)) {\n" +
                        "      formattedDateTime = formattedDateTime.replace(token, formatTokens[token]);\n" +
                        "    }\n" +
                        "  }\n" +
                        "\n" +
                        "  return formattedDateTime;\n" +
                        "}\n" +
                        "\n" +
                        "function padZero(value) {\n" +
                        "  return value.toString().padStart(2, '0');\n" +
                        "}\n    " +
                "let extractNumbers = function(text) {\n" +
                "        const regex = /\\d+(\\.\\d+)?/g;\n" +
                "        const numbers = text.match(regex);\n" +
                "        if (numbers) {\n" +
                "            return numbers.map(Number)[0];\n" +
                "        } else {\n" +
                "            return 0;\n" +
                "        }\n" +
                "    }\n   " +
                "let extractEmail = function(text){\n" +
                "        const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g;\n" +
                "        const emails = text.match(emailRegex);\n" +
                "        // console.log(\"邮箱地址:\", emails);\n" +
                "        if(emails && emails.length > 0){\n" +
                "            return emails[0];\n" +
                "        }else{\n" +
                "            return '';\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "    let extractPhone = function(text){\n" +
                "        const phoneRegex = /((\\d{3}-\\d{8})|(\\d{4}-\\d{7,8})|((13\\d|14[5-9]|15[0-3,5-9]|16[6]|17[0-8]|18\\d|19[8-9])\\d{8}))/g;\n" +
                "        const phone = text.match(phoneRegex);\n" +
                "        if(phone && phone.length > 0){\n" +
                "            return phone[0];\n" +
                "        }else{\n" +
                "            return '';\n" +
                "        }\n" +
                "\n" +
                "    }\n" ;
    }

    @Override
    protected QueryWrapper<OutputTemplate> getQueryWrapper(OutputTemplateQuery query) {
        QueryWrapper<OutputTemplate> queryWrapper = super.getQueryWrapper(query);
        return queryWrapper;
    }

    public Pager<OutputTemplateVo> findVoByPage(OutputTemplateQuery query){
        Pager<OutputTemplate> page = this.findByPage(query);
        Pager<OutputTemplateVo> newPage = new Pager<>(page);
        List<OutputTemplate> dataList = page.getDataList();
        List<OutputTemplateVo> newDataList = new ArrayList<>();
        newPage.setDataList(newDataList);
        for (OutputTemplate outputTemplate : dataList) {
            OutputTemplateVo outputTemplateVo = BeanUtils.beanCopy(outputTemplate, OutputTemplateVo.class);
            newDataList.add(outputTemplateVo);
        }
        return newPage;
    }


    public void outputSingle(OutputContext context, ActiveInstance activeInstance, OutputTemplate outputTemplate, CrawlerWebDriver crawlerWebDriver){
        if (StringUtils.isEmpty(activeInstance.getResponseAnalyseBody())) {
            return ;
        }
        WebDriver webDriver = crawlerWebDriver.getWebDriver();
        ActiveProcess activeProcess = activeProcessService.findById(activeInstance.getActiveProcessId());
        //get packet
        String script = "var dataSource = "+activeInstance.getResponseAnalyseBody()+";\n";
        if (StringUtils.isNotEmpty(activeProcess.getVariables())) {
            script += "var variables = "+activeProcess.getVariables()+";\n";
        }
        script += baseScript;
        script += outputTemplate.getPacket();
        Object jsResult = crawlerWebDriverBuilder.executeScript(webDriver, script);
        if(jsResult == null || StringUtils.isEmpty(jsResult.toString())){
            return;//没有找到数据
        }
        if (outputTemplate.getType().equals(OutputTemplateTypeEnum.HTTP.getCode())) {
            String resultPacket;
            if(jsResult instanceof Map){
                resultPacket = JsonUtil.bean2Json(jsResult);
            }else{
                resultPacket = JsonUtil.list2Json(jsResult);
            }
            Map<String, String> headerMap = new HashMap<>();
            String header = outputTemplate.getHeader();
            if (StringUtils.isNotEmpty(header)) {
                headerMap = JsonUtil.json2Bean(header,Map.class);
            }
            RequestRes requestRes = new RequestRes();
            requestRes.setMethod(outputTemplate.getMethod());
            requestRes.setHeaders(headerMap);
            requestRes.setUrl(outputTemplate.getUrl());
            requestRes.setParamText(resultPacket);
            ResponseRes responseRes = HttpNewUtils.execute(requestRes);
            logger.info("http execute finished! activeInstanceId={},responseRes={}", activeInstance.getId(), responseRes);

        } else if (outputTemplate.getType().equals(OutputTemplateTypeEnum.JDBC.getCode())) {
            String resultPacket = jsResult.toString();
            int result = databaseComponent.send(outputTemplate.getId(), DatabaseTypeEnum.MYSQL.getCode(), resultPacket);
            logger.info("jdbc execute finished! activeInstanceId={},result={}", activeInstance.getId(), result);
        } else if (outputTemplate.getType().equals(OutputTemplateTypeEnum.EXCEL.getCode())) {
            String resultPacket = JsonUtil.list2Json(jsResult);
            if(context.getIndex() == 1){
                String filePath = ExcelGenerator.write(outputTemplate.getName(), outputTemplate.getUrl(), outputTemplate.getHeader(), resultPacket);
                context.setXlsFilePath(filePath);
            }else{
               ExcelGenerator.writeAppend(context.getXlsFilePath(),resultPacket);
            }
            logger.info("excel execute finished! activeInstanceId={}", activeInstance.getId());
        }

    }


}
